#include "./cmd_uart.h"
#include "../xlib/gpio.h"
#include "../xlib/uart.h"
#include "../rtos/event_flags.h"
#include "../rtos/message_queue.h"
#include "../pub_driver/proto_recv_drv.h"
#include <stdio.h>

namespace main_driver {

CmdUart cmdUart;

namespace cmd_uart_impl {

using namespace stm32f1;
using namespace rtos::lazy;
using namespace pub_driver;

using PIN_TX = gpio::PA<9>;
using PIN_RX = gpio::PA<10>;
using UART = uart::Uart1;
using DMA_TX = typename dma::DmaSel<UART::BASE_VAL>::TX_CHAN;
using DMA_RX = typename dma::DmaSel<UART::BASE_VAL>::RX_CHAN;

static constexpr uint32_t RECV_BUF_SIZE = 512;
static EventFlags eventFlags;
static MessageQueue<ByteView, 10> msgQueue;
static UartDmaRecvBuff<decltype(msgQueue), DMA_RX, RECV_BUF_SIZE> dmaRecvBuf(msgQueue);
static ProtoRecvDrv<decltype(msgQueue), 20, 512> protoRecvDev(msgQueue);

constexpr uint32_t FLAG_WRITE_FINISH = 0x01;

}

using namespace cmd_uart_impl;

void CmdUart::init() {
    eventFlags.init();
    msgQueue.init();
    eventFlags.set_flags(FLAG_WRITE_FINISH);

    rcc::enable<PIN_TX, PIN_RX, UART, DMA_TX, DMA_RX>();
    gpio::config<
        gpio::Cfg<PIN_TX, gpio::Mode::OUT_50MHz_AF_PP>,
        gpio::Cfg<PIN_RX, gpio::Mode::INPUT_FLOAT>
    >();
    UART::set_cfg<uart::DefCfg>();
    uart::recv_with_dma<UART>(dmaRecvBuf.buf, RECV_BUF_SIZE);
    NVIC_EnableIRQ(USART1_IRQn);
    NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}   

void CmdUart::wait_write_finish() {
    eventFlags.wait_any_flags(FLAG_WRITE_FINISH);
}

void CmdUart::write_raw(const uint8_t *buf, usize_t len) {
    uart::write_with_dma<UART>(buf, len);
}

void CmdUart::write(const uint8_t *buf, usize_t len) {
    wait_write_finish();
    uart::write_with_dma<UART>(buf, len);
}

void CmdUart::recv_from(conn::Frame &frame) {
    protoRecvDev.recv_from(frame);
}

void CmdUart::recv_next(usize_t len) {
    protoRecvDev.recv_next(len);
}

void CmdUart::log(const char *format, va_list ap) {
    using namespace conn;

    wait_write_finish();
    int n = vsprintf(
        reinterpret_cast<char *>(m_write_buf + 8),
        format,
        ap
    );
    m_write_buf[6] = conn::proto::pub::NOTIFY_LOG;
    m_write_buf[7] = n;
    usize_t len = make_proto_data<conn::type::NOTIFY>(m_write_buf, addr::IPC, 2 + n);
    write_raw(m_write_buf, len);
}

}

using namespace main_driver;

extern "C"
void USART1_IRQHandler() {
    if (UART::is_idle()) {
        UART::clear_idle_flag();
        dmaRecvBuf.on_recv_idle();
    }
}

extern "C"
void DMA1_Channel4_IRQHandler() {
    if (DMA_TX::is_tc()) {
        DMA_TX::clear_flags();
        eventFlags.set_flags(FLAG_WRITE_FINISH);
    }
}

